﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

public class LuaNetMgr
{
    private const int MSG_LIST_LEN = 10000;
    private const int RECV_BUF_LEN = 11 * 1024 * 1024;
    public delegate int MsgSvrDispatcher(LuaMsg msg);

    public LuaMsg[] recvMsgBufList;
    public LuaMsg[] sendMsgBufList;
    public LuaMsg[] recvMsgList;
    private LuaMsg[] sendMsgList;

    public int recvMsgBufCount;
    public int sendMsgBufCount;
    public int recvMsgCount;
    private int sendMsgCount;

    private int recvBeginIdx;
    private int recvEndIdx;
    private int recvMsgPacketLen;
    private int recvMsgContentLen;

    private Thread mRecvThread;
    private Thread mSendThread;
    private byte[] recvDataBuf;
    List<LuaMsg> recvTempMsgList;
    
    private TcpClient mTransceiver;
    private NetworkStream mStream;
    private bool mStartIOThread;
    public string MgrName { get; set; }

    public LuaNetMgr(bool bStartIOThread)
    {
        recvMsgBufCount = 0;
        sendMsgBufCount = 0;
        recvMsgCount = 0;
        sendMsgCount = 0;

        recvMsgBufList = new LuaMsg[MSG_LIST_LEN];
        sendMsgBufList = new LuaMsg[MSG_LIST_LEN];
        recvMsgList = new LuaMsg[MSG_LIST_LEN];
        sendMsgList = new LuaMsg[MSG_LIST_LEN];

        recvDataBuf = new byte[RECV_BUF_LEN];
        recvTempMsgList = new List<LuaMsg>();

        MgrName = "";
        recvBeginIdx = 0;
        recvEndIdx = 0;
        recvMsgContentLen = 0;
        mStartIOThread = bStartIOThread;

    }
    
    public void Connect(string ip, int port)
    {
        IPAddress ipAddress = IPAddress.Parse(ip);
        mTransceiver = new TcpClient();
        mTransceiver.ReceiveBufferSize = RECV_BUF_LEN;
        mTransceiver.SendBufferSize = RECV_BUF_LEN;
        mTransceiver.NoDelay = true;
        mTransceiver.Connect(ipAddress, port);
        mStream = mTransceiver.GetStream();

        if (mStartIOThread)
        {
            mRecvThread = new Thread(StartRecvData);
            mRecvThread.Name = MgrName + "接收线程";
            mRecvThread.Start();

            mSendThread = new Thread(StartSendData);
            mSendThread.Name = MgrName + "发送线程";
            mSendThread.Start();
        }
    }
    private void StartRecvData()
    {
        for (; ; )
        {
            RecvData();
        }
    }

    private void RecvData()
    {
        int count = 0;
        if (mStartIOThread)
        {
            count = mStream.Read(recvDataBuf, recvEndIdx, recvDataBuf.Length - recvEndIdx);
        }
        else
        {
            if (mStream.DataAvailable)
            {
                count = mStream.Read(recvDataBuf, recvEndIdx, recvDataBuf.Length - recvEndIdx);
            }
        }
        if (count > 0)
        {
            recvEndIdx += count;
            int dataLen = recvEndIdx - recvBeginIdx;
            for (; ; )
            {
                if (recvMsgContentLen == 0)
                {
                    if (dataLen < 4)
                    {
                        break;
                    }
                    recvMsgPacketLen = BitConverter.ToInt32(recvDataBuf, recvBeginIdx);
                    recvBeginIdx += 4;
                    dataLen = recvEndIdx - recvBeginIdx;
                    recvMsgContentLen = recvMsgPacketLen - 4;
                }
                if (recvMsgContentLen == 0)
                {
                    continue;
                }
                if (recvMsgContentLen > dataLen)
                {
                    break;
                }
                LuaMsg msg = LuaMsg.DeserializeMsg(recvDataBuf, recvBeginIdx, recvMsgContentLen);
                if (msg != null)
                {
                    recvTempMsgList.Add(msg);
                }
                recvBeginIdx += recvMsgContentLen;
                dataLen = recvEndIdx - recvBeginIdx;
                recvMsgContentLen = 0;
            }
            Array.Copy(recvDataBuf, recvBeginIdx, recvDataBuf, 0, dataLen);
            recvBeginIdx = 0;
            recvEndIdx = dataLen;
            
            int newMsgCount = recvTempMsgList.Count;
            if (newMsgCount > 0)
            {

                LuaMsg[] newMsgList = recvTempMsgList.ToArray();
                int index = 0;
                for (; ; )
                {
                    int copyCount = CopyToRecvMsgBufList(newMsgList, ref index, ref newMsgCount);
                    if (copyCount <= 0)
                    {
                        if (mStartIOThread)
                        {
                            Thread.Sleep(1);
                            continue;
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (newMsgCount <= 0)
                    {
                        break;
                    }
                }
                recvTempMsgList.Clear();
            }
        }
    }

    private int CopyToRecvMsgBufList(LuaMsg[] newMsgList, ref int index, ref int newMsgCount)
    {
        int count = 0;
        int freeSize = 0;
        lock (recvMsgBufList)
        {
            freeSize = recvMsgBufList.Length - recvMsgBufCount;
            if (freeSize > 0)
            {
                if (newMsgCount <= freeSize)
                {
                    Array.Copy(newMsgList, index, recvMsgBufList, recvMsgBufCount, newMsgCount);
                    recvMsgBufCount += newMsgCount;
                    index += newMsgCount;
                    count = newMsgCount;
                    newMsgCount -= newMsgCount;
                }
                else
                {
                    Array.Copy(newMsgList, index, recvMsgBufList, recvMsgBufCount, freeSize);
                    recvMsgBufCount += freeSize;
                    index += freeSize;
                    count = freeSize;
                    newMsgCount -= freeSize;
                }
            }
            else
            {
                count = 0;
            }
        }
        return count;
    }

    public int RecvMsg(int nMaxProcessCount, MsgSvrDispatcher vDispatcher)
    {
        if (!mStartIOThread)
        {
            RecvData();
        }
        int recvCount = 0;

        int freeSize = MSG_LIST_LEN - recvMsgCount;
        if (freeSize > 0)
        {
            lock (recvMsgBufList)
            {
                if (recvMsgBufCount > 0)
                {
                    if (recvMsgBufCount <= freeSize)
                    {
                        Array.Copy(recvMsgBufList, 0, recvMsgList, recvMsgCount, recvMsgBufCount);
                        recvCount = recvMsgBufCount;
                        recvMsgCount += recvMsgBufCount;
                        recvMsgBufCount = 0;
                    }
                    else
                    {
                        Array.Copy(recvMsgBufList, 0, recvMsgList, recvMsgCount, freeSize);
                        recvCount = freeSize;
                        recvMsgCount += freeSize;
                        recvMsgBufCount -= freeSize;
                        Array.Copy(recvMsgBufList, freeSize, recvMsgBufList, 0, recvMsgBufCount);
                    }
                }
            }
        }

        LuaMsg[] msgList = recvMsgList;
        int processCount = 0;
        if (recvMsgCount > nMaxProcessCount)
        {
            processCount = nMaxProcessCount;
        }
        else
        {
            processCount = recvMsgCount;
        }
        for (int i = 0; i < processCount; i++)
        {
            vDispatcher(msgList[i]);
        }
        recvMsgCount -= processCount;
        if (recvMsgCount > 0)
        {
            Array.Copy(recvMsgList, processCount, recvMsgList, 0, recvMsgCount);
        }
        return processCount;
    }

    private void StartSendData()
    {
        for (; ; )
        {
            SendData();
            if (sendMsgCount == 0)
            {
                Thread.Sleep(1);
            }
        }
    }

    private void SendData()
    {
        sendMsgCount = 0;
        lock (sendMsgBufList)
        {
            if (sendMsgBufCount > 0)
            {
                Array.Copy(sendMsgBufList, sendMsgList, sendMsgBufCount);
                sendMsgCount = sendMsgBufCount;
                sendMsgBufCount = 0;
            }
        }

        for (int i = 0; i < sendMsgCount; i++)
        {
            byte[] data = LuaMsg.SerializeMsg(sendMsgList[i]);
            if (data == null) { continue; }
            mStream.Write(data, 0, data.Length);
        }
    }


    public void SendMsg(LuaMsg msg)
    {
        for (; ; )
        {
            lock (sendMsgBufList)
            {
                if (sendMsgBufCount < MSG_LIST_LEN)
                {
                    sendMsgBufList[sendMsgBufCount] = msg;
                    sendMsgBufCount++;
                    break;
                }
                else
                {
                    if (!mStartIOThread)
                    {
                        break;
                    }
                }
            }

        }
        if (!mStartIOThread)
        {
            SendData();
        }
    }

    public void CloseConnect()
    {
        if (mTransceiver != null)
        {
            if (mStartIOThread)
            {
                //关闭线程
                mSendThread.Abort();
                mRecvThread.Abort();
            }
            mTransceiver.Close();
        }
    }
}